#include "db/segmentdataparser.hxx"
#include "util/serialize.hxx"
#include "dbt.hxx"
#include <string.h>

bool SegmentDataParser::getValueByKey(const std::string& key, const std::string&
        data, std::string& value) {

	Deserializer des(data);
	
	while(des.curPos() < data.size()) {

		bool found = false;
		uint32_t len = des.readInt(); 
		if(key.size() == len &&  memcmp(key.c_str(), data.c_str()+des.curPos(),
                    key.size()) == 0) {
		
			found = true;
		}

		des.skip(len);

		len = des.readInt();
		if(found) {
		
			value = des.readString(len);
			return true;
		}
		else des.skip(len);
	}

	return false;
}

//value shoule have empty buffer of have enough buffer space to hold the real
//data value; 
//if found:if value dbt has enough buffer space, put the value in the value dbt and 
//return true. if value dbt is empty, allocate a new buffer and let value dbt refer the new buffer.
//if not found, return false.  
bool SegmentDataParser::getValueByKeyDbt(Dbt& key, Dbt& data, Dbt& value) {
    
    DbStatus ret;
    Deserializer des(data.data(), data.getDatalen());
    

    while(des.curPos() < data.getDatalen()) {
    
        bool found = false;
        uint32_t len = des.readInt();
        if(key.getDatalen() == len &&  memcmp(key.data(), data.data() +
                    des.curPos(), len) == 0) {

            found = true;
        }

        des.skip(len);

        len = des.readInt();
        if(found) {

            if(value.empty()) {

               char* buf = new char[len];
               value.setBuffer(buf, len, 0, DBT_MALLOC);
            }
            
            des.readDbt(len, value);
            value.setDatalen(len);
            return true;
        }
        else des.skip(len);
    }

    return false;
}

void SegmentDataParser::removeByKey(const std::string& key, std::string& data) {

	Deserializer des(data);  
    uint32_t to; bool found = false;
	
	while(des.curPos() < data.size()) {
		
		uint32_t len = des.readInt(); 
		if(key.size() == len && memcmp(key.c_str(), data.c_str()+des.curPos(),
                    key.size()) == 0) {
		
			found = true; to = des.curPos() - sizeof(uint32_t);
		}

		des.skip(len); 

		len = des.readInt();
		des.skip(len);

		if(found) break;
	}

	if(found) {
	
		char *buf = (char*)data.c_str(); uint32_t from = des.curPos();
		memmove(buf + to, buf + from, data.size() - from);
		data.erase(data.size() - (from - to), from - to);
	}
}


void SegmentDataParser::removeByKeyDbt(Dbt& key, Dbt& data) {
    
    Deserializer des(data.data(), data.getDatalen());  
    uint32_t to; bool found = false;

    while(des.curPos() < data.getDatalen()) {

        uint32_t len = des.readInt();
        if(key.getDatalen() == len && memcmp(key.data(), data.data() +
                    des.curPos(), len) == 0) {
        
            found = true; to = des.curPos() - sizeof(uint32_t);
        }

        des.skip(len);

        len = des.readInt();
        des.skip(len);

        if(found) break;
    }

    if(found) {
    
        char *buf = (char*)data.data(); uint32_t from = des.curPos();
        memmove(buf + to, buf + from, data.getDatalen() - from);
        data.setDatalen(to + data.getDatalen() - from);
    }

}


std::string SegmentDataParser::serialize(const std::string& key, const std::string& value) {

    std::string data;

	uint32_t len = key.size();
	data.append((char*)&len, sizeof(len));
	data.append(key);
	len = value.size();
    data.append((char*)&len, sizeof(len));
	data.append(value);

	return data;
}


//case 1: if result has enough space, return result. In the case, caller does't need release memory
//case 2: if result has no enough spave, allocate new buffer and return new dbt. 
//In the case, result doesn't include data. Caller need to release memory
Dbt SegmentDataParser::serializeDbt(Dbt& key, Dbt& value, Dbt& result) {

    Dbt dbt = result;
    bool newbuffer = false;

    uint32_t bufSize = sizeof(uint32_t)*2 + key.getDatalen() +
        value.getDatalen();

    if(result.empty() || result.getSize() < bufSize) {
        
        char* buf = new char[bufSize]; newbuffer = true;
        dbt.setBuffer(buf, bufSize, 0, DBT_MALLOC);
    }


    DbtSerializer des(&dbt);

    des.appendDbt(key);
    des.appendDbt(value);

    if(!newbuffer) result = dbt;

    return dbt;
}


std::string SegmentDataParser::serialize(const std::string& key, const std::string& value, std::string& data) {

	SegmentDataParser::removeByKey(key, data);

	std::string val = SegmentDataParser::serialize(key, value);
	data.append(val);

	return data;
}

//case 1: if 'data' has enough space to hold key/value, return 'data'. In the case, caller does't need release memory
//case 2: if 'data' has no enough spave, allocate new buffer and return new dbt. 
//In the case, 'data' doesn't include the final data. Caller need to release memory
Dbt SegmentDataParser::serializeDbtFromDbt(Dbt& key, Dbt& value, Dbt& data) {

    bool newbuffer = false;

    SegmentDataParser::removeByKeyDbt(key, data);

    uint32_t bufSize = sizeof(uint32_t)*2 + key.getDatalen() +
        value.getDatalen() + data.getDatalen();
    Dbt dbt = data;

    if(data.empty() || data.getSize() < bufSize) {

        char* buf = new char[bufSize]; newbuffer = true;
        dbt.setBuffer(buf, bufSize, 0, DBT_MALLOC);
        memcpy(buf, data.data(), data.getDatalen());
        dbt.setDatalen(data.getDatalen());
    }

    Dbt tmpdbt(dbt.data() + dbt.getDatalen(), dbt.getSize() - dbt.getDatalen(), 0, 0); 
    SegmentDataParser::serializeDbt(key, value, tmpdbt);
    dbt.setDatalen(dbt.getDatalen() + tmpdbt.getDatalen());

    if(!newbuffer) data = dbt;

    return dbt;
}

